import React, { useState, useEffect, useMemo, useRef } from 'react';
import { Button, Card, Result, Spin, Typography } from '@arco-design/web-react';
import styles from './style/index.module.less';
import throttle from '@/utils/throttle';
import SearchForm from './searchForm';
import * as constants from './constants';
import classNames from 'classnames';
import Api from '@/services/api';
import { IconEdit } from '@arco-design/web-react/icon';
import { DetailModal } from './detailModal';
import { MultipalModal } from './multipalModal';
const { Text } = Typography;

interface point {
  x: number;
  y: number;
  d?: number;
  t?: number;
}

const boxSize = constants.boxSize;
const axisSize = constants.axisSize;
const overflowPointsCount = constants.overflowPointsCount;

function Page() {
  const [points, setPoints] = useState<Record<string, point>>({});
  const [axisXs, setAxisXs] = useState([]);
  const [axisYs, setAxisYs] = useState([]);

  const factoryLengthRef = useRef<number>(0);
  const [visiblePoints, setVisiblePoints] = useState<point[]>([]);
  const [movement, setMovement] = useState({ x: -axisSize, y: -axisSize });
  const [isMoving, setIsMoving] = useState(false);
  const [screenViewPort, setScreenViewPort] = useState({ x: 0, y: 0 });

  const [loading, setLoading] = useState(false);
  const [activePoint, setActivePoint] = useState<point>({ x: -1, y: -1 });

  const [isMetrixMoving, setMetrixMoving] = useState(false);
  const movementRef = useRef<{ x: number; y: number; movement: any }>();
  const viewportRef = useRef<HTMLDivElement>();
  const matrixRef = useRef<HTMLDivElement>();
  const getMatrixListRef = useRef<NodeJS.Timeout>();

  const [pointDetail, setPointDetail] = useState<point | false>(false);
  const [showMultipalDetailModal, setShowMultipalDetailModal] = useState(false);

  useEffect(() => {
    matrixRef.current.style.setProperty('--matrix-box-size', `${boxSize}px`);
    matrixRef.current.style.setProperty('--matrix-axis-size', `${axisSize}px`);
  }, []);

  useEffect(() => {
    setLoading(true);
    Api.getMatrixList()
      .then((res) => {
        setPoints(setPointsFromRemoteData(res.list));
        setAxisXs(res.axisX);
        setAxisYs(res.axisY);
        factoryLengthRef.current = res.total;

        setScreenViewPort({ ...movement });
        setLoading(false);
      })
      .catch(() => {
        setLoading(false);
      });
  }, []);

  const setPointsFromRemoteData = (list) => {
    const initPoints = {};
    list.forEach((e) => {
      const key = `x:${e.x},y:${e.y}`;
      initPoints[key] = e;
    });
    return initPoints;
  };

  const getMatrixLocationsData = async (noDataPoints) => {
    // setLoading(true);
    console.log('请求缺失数据', noDataPoints);
    clearTimeout(getMatrixListRef.current);
    getMatrixListRef.current = setTimeout(() => {
      noDataPoints.forEach((f) => {
        if (!f.d && !f.t) {
          f.d = Math.floor(Math.random() * 1000);
          f.t = Math.floor(Math.random() * 1000);
        }
      });

      setPoints({
        ...points,
        ...setPointsFromRemoteData(noDataPoints),
      });
      clearTimeout(getMatrixListRef.current);
      // setLoading(false);
    }, 1000);
  };

  // 处理移动操作集合
  const handleMoveEvent = () => {
    const handleMouseMove = throttle((event: any) => {
      const screenX =
        event.type === 'touchmove'
          ? event.targetTouches?.[0]?.screenX
          : event.screenX;
      const screenY =
        event.type === 'touchmove'
          ? event.targetTouches?.[0]?.screenY
          : event.screenY;
      if (movementRef.current) {
        const diffX = screenX - movementRef.current.x;
        const diffY = screenY - movementRef.current.y;

        let x = movementRef.current.movement.x - diffX;
        let y = movementRef.current.movement.y - diffY;

        if (constants.isMoveLimit) {
          x = Math.ceil(x / boxSize) * boxSize - axisSize;
          y = Math.ceil(y / boxSize) * boxSize - axisSize;
        }
        moveTo(x, y);
      }
    }, 50);
    const handleMouseDown = throttle((event: any) => {
      const screenX =
        event.type === 'touchstart'
          ? event.targetTouches?.[0]?.screenX
          : event.screenX;
      const screenY =
        event.type === 'touchstart'
          ? event.targetTouches?.[0]?.screenY
          : event.screenY;
      movementRef.current = {
        x: screenX,
        y: screenY,
        movement: { ...movement },
      };
      setIsMoving(true);
    }, 50);
    const handleMouseUp = throttle((event: any) => {
      if (movementRef.current) {
        movementRef.current = null;
        setScreenViewPort({
          ...movement,
        });
        setIsMoving(false);
      }
    }, 1000 / 60);
    return {
      onMouseUp: handleMouseUp,
      onMouseLeave: handleMouseUp,
      onMouseDown: handleMouseDown,
      onMouseMove: handleMouseMove,
    };
  };

  // 获取移动操作集合
  const movementEvents = handleMoveEvent();

  /*
   * 视窗移动后的副作用
   * 1. 计算视窗中可见的坐标
   * 2. 获取可见范围内无数据的坐标并获取数据
   * 3. 重新更新所有点以及可见坐标
   */
  useEffect(() => {
    if (viewportRef.current && factoryLengthRef.current) {
      const noDataPoints = [];
      const _startXIndex = Math.max(
        0,
        Math.ceil(movement.x / boxSize - overflowPointsCount)
      );
      const _endXIndex = Math.min(
        factoryLengthRef.current,
        Math.floor(
          (movement.x + viewportRef.current.offsetWidth) / boxSize +
            overflowPointsCount
        )
      );
      const _startYIndex = Math.max(
        0,
        Math.ceil(movement.y / boxSize - overflowPointsCount)
      );
      const _endYIndex = Math.min(
        factoryLengthRef.current,
        Math.floor(
          (movement.y + viewportRef.current.offsetHeight) / boxSize +
            overflowPointsCount
        )
      );
      const _visiblePoints = [];
      for (let i = _startXIndex; i <= _endXIndex; i++) {
        for (let j = _startYIndex; j <= _endYIndex; j++) {
          const _initPoint = { x: i, y: j };
          const _point = points[`x:${i},y:${j}`];
          if (!_point) {
            noDataPoints.push(_initPoint);
          }
          _visiblePoints.push(_point || _initPoint);
        }
      }
      if (noDataPoints.length > 0) {
        getMatrixLocationsData(noDataPoints);
      }
      setVisiblePoints(_visiblePoints);
    }
  }, [points, screenViewPort]);

  // 渲染X轴
  const axisXRender = useMemo(() => {
    if (viewportRef.current) {
      return axisXs
        .filter((e) => {
          return (
            (e.i + overflowPointsCount) * boxSize > movement.x &&
            (e.i - overflowPointsCount) * boxSize <
              movement.x + viewportRef.current.offsetWidth
          );
        })
        .map((e) => (
          <span
            key={e.n}
            className={classNames(styles.unit, {
              [styles.relative]: activePoint?.x === e.i,
            })}
            style={{
              left: e.i * boxSize + 'px',
              top: 0,
            }}
          >
            {e.n}
          </span>
        ));
    } else {
      return false;
    }
  }, [axisXs, screenViewPort, activePoint]);

  // 渲染Y轴
  const axisYRender = useMemo(() => {
    if (viewportRef.current) {
      return axisYs
        .filter((e) => {
          return (
            (e.i + overflowPointsCount) * boxSize > movement.y &&
            (e.i - overflowPointsCount) * boxSize <
              movement.y + viewportRef.current.offsetHeight
          );
        })
        .map((e) => {
          return (
            <span
              key={e.n}
              className={classNames(styles.unit, styles.lt, {
                [styles.relative]: activePoint?.y === e.i,
              })}
              style={{
                top: e.i * boxSize + 'px',
                left: 0,
              }}
            >
              {e.n}
            </span>
          );
        });
    } else {
      return false;
    }
  }, [axisYs, screenViewPort, activePoint]);

  // 渲染可见坐标
  const visiblePointesRender = useMemo(() => {
    return (
      <>
        {visiblePoints?.map((point) => {
          const isActive =
            activePoint?.x === point.x && activePoint?.y === point.y;
          return (
            <div
              key={`x:${point.x},y:${point.y}`}
              className={classNames(
                styles.item,
                {
                  [styles.relative]:
                    activePoint?.x === point.x || activePoint?.y === point.y,
                },
                {
                  [styles.active]: isActive,
                }
              )}
              style={{
                left: point.x * boxSize + 'px',
                top: point.y * boxSize + 'px',
              }}
              onClick={() => setActivePoint(point)}
            >
              {point.d ? (
                <>
                  <Text>{point.d}千米</Text>
                  <Text>{point.t}小时</Text>
                  {isActive && (
                    <div
                      className={styles.itemEdit}
                      onClick={() => setPointDetail(point)}
                    >
                      <IconEdit />
                    </div>
                  )}
                </>
              ) : (
                <Text>数据获取中...</Text>
              )}
            </div>
          );
        }) || false}
      </>
    );
  }, [visiblePoints, activePoint]);

  // 搜索坐标
  const handleSearch = (params) => {
    if (
      (boxSize * params.x || boxSize * params.x === 0) &&
      (boxSize * params.y || boxSize * params.y === 0)
    ) {
      const x = boxSize * params.x - axisSize;
      const y = boxSize * params.y - axisSize;

      setMetrixMoving(true);
      setTimeout(() => {
        setMetrixMoving(false);
      }, 500);
      setScreenViewPort(moveTo(x, y));
      setActivePoint({ x: params.x, y: params.y });
    }
  };

  // 移动到坐标位置
  const moveTo = (x, y) => {
    // x轴最远端
    const maxX =
      boxSize * factoryLengthRef.current - viewportRef.current.offsetWidth + 2;
    // y轴最远端
    const maxY =
      boxSize * factoryLengthRef.current - viewportRef.current.offsetHeight + 2;
    // x轴最近端
    const minX = -axisSize;
    // y轴最近端
    const minY = -axisSize;
    const position = {
      x: Math.min(maxX, Math.max(minX, x)),
      y: Math.min(maxY, Math.max(minY, y)),
    };
    setMovement(position);
    return position;
  };

  const searchFormRender = useMemo(() => {
    return (
      <SearchForm onSearch={handleSearch} axisXs={axisXs} axisYs={axisYs}>
        <Button
          type="primary"
          status="danger"
          onClick={() => setShowMultipalDetailModal(true)}
        >
          <IconEdit />
          批量修改
        </Button>
      </SearchForm>
    );
  }, [axisXs, axisYs]);

  return (
    <Card>
      {searchFormRender}
      <div
        className={classNames(styles.matrix, {
          [styles.moving]: isMetrixMoving,
        })}
        ref={matrixRef}
      >
        <div className={styles.viewport} ref={viewportRef}>
          {axisXRender && axisYRender && <div className={styles.corner}></div>}
          <div
            className={classNames(styles.container, isMoving && styles.moving)}
            style={{
              transform: `translate3d(${-movement.x}px,${-movement.y}px,0)`,
              width: boxSize * factoryLengthRef.current,
              height: boxSize * factoryLengthRef.current,
            }}
            {...movementEvents}
          >
            {visiblePointesRender}
          </div>
          <div
            className={classNames(styles.container, isMoving && styles.moving)}
            style={{
              transform: `translate3d(${-movement.x}px,0px,0)`,
              width: boxSize * axisXs.length,
              height: axisSize,
              background: 'var(--color-fill-2)',
            }}
            {...movementEvents}
          >
            {axisXRender}
          </div>
          <div
            className={classNames(styles.container, isMoving && styles.moving)}
            style={{
              transform: `translate3d(0px,${-movement.y}px,0)`,
              height: boxSize * axisYs.length,
              width: axisSize,
              background: 'var(--color-fill-2)',
            }}
            {...movementEvents}
          >
            {axisYRender}
          </div>
        </div>
        <div className={[styles.loadWrap, loading && styles.loading].join(' ')}>
          <Spin />
        </div>
      </div>

      <DetailModal
        onCancel={() => setPointDetail(false)}
        onOk={(params) => {
          setPointDetail(false);
          setPoints({
            ...points,
            ...setPointsFromRemoteData([params]),
          });
        }}
        pointInfo={pointDetail}
      />
      <MultipalModal
        onCancel={() => setShowMultipalDetailModal(false)}
        onOk={(params) => {
          setShowMultipalDetailModal(false);
          setPoints({
            ...points,
            ...setPointsFromRemoteData([params]),
          });
        }}
        visible={showMultipalDetailModal}
        axisXs={axisXs}
        axisYs={axisYs}
      />
    </Card>
  );
}

export default Page;
